﻿using Core.Framework.EntityExtend.Model;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;

namespace Core.Framework.EntityExtend
{
    public static class Exposure
    {

        #region Update

        /// <summary>
        /// 更新忽略UNLL字段
        /// </summary>
        /// <param name="context">Context.</param>
        /// <typeparam name="T">The 1st type parameter.</typeparam>
        public static void ExcludeNullField<T>(this DbContext context, T model) where T : class
        {
            foreach (PropertyInfo p in model.GetType().GetProperties())
            {
                var isNull = p.GetValue(model) != null;
                try
                {
                    context.Entry<T>(model).Property(p.Name).IsModified = isNull;
                }
                catch
                {
                }
            }
        }

        /// <summary>
        /// 更新数据条件
        /// </summary>
        /// <typeparam name="T"></typeparam>
        public static void Where<T>(this IResult<T> iResult, Expression<Func<T, bool>> where)
            where T : class, new()
        {
            if (iResult is UpdateResult<T>)
            {
                if (EFCoreTaskParameter.dictionary.ContainsKey(iResult.context))
                {
                    var tuple = EFCoreCommon.GetWhereStr(iResult.context, where);
                    if (iResult.Fields.Count != 0)
                    {

                        var sqllist = EFCoreTaskParameter.dictionary[iResult.context];

                        for (int i = 0; i < sqllist.Count; i++)
                            if (sqllist[i].Trim() == iResult.sql.Trim())
                                sqllist.Remove(sqllist[i]);

                        EFCoreTaskParameter.dictionary[iResult.context].Remove(iResult.sql);

                        EFCoreTaskParameter.dictionary[iResult.context].Add($"{iResult.sql} where {tuple.Item2}");
                    }
                }
                else
                {
                    throw new Exception("未找到当前上下文");
                }

            }
        }

        /// <summary>
        /// 更新数据源
        /// </summary>
        /// <returns></returns>
        public static IResult<T> BulkUpdate<T>(this DbSet<T> model, T entity)
            where T : class, new()
        {
            if (entity == null)
                return new UpdateResult<T>();

            
            var context = (DbContext)EFCoreCommon.GetValueByField(model.Local, "_context");

            // 字段解析
            var mapping = EFCoreModel<T>.GetModelMapping(context);

            // 构造返回值
            var result = new UpdateResult<T> { context = context };

            // 构造 修改字段
            foreach (PropertyInfo p in entity.GetType().GetProperties())
            {
                var val = p.GetValue(entity)?.ToString();

                if (val != null
                    && !string.IsNullOrWhiteSpace(val)
                    && !mapping.rows[p.Name].isIdentity)
                    result.Fields.Add(new Tuple<string, string>(p.Name, val));
            }

            // 修改字段为0 则直接返回
            if (result.Fields.Count == 0)
                return result;

            // 构造 操作数据
            result.sql =
                $"update {mapping.tableName} " +
                $"set {string.Join(',', result.Fields.Select(a => $"{a.Item1} = '{a.Item2}'"))} ";

            // 写入当前上下文
            if (EFCoreTaskParameter.dictionary.ContainsKey(result.context))
                EFCoreTaskParameter.dictionary[result.context].Add(result.sql);
            else
                EFCoreTaskParameter.dictionary.TryAdd(result.context, new List<string>() { result.sql });

            return result;
        }

        #endregion

        #region Insert

        /// <summary>
        /// 批量插入
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="table"></param>
        /// <param name="where"></param>
        public static void BulkInsert<T>(this DbSet<T> model, List<T> list)
            where T : class, new()
        {

            var context = (DbContext)EFCoreCommon.GetValueByField(model.Local, "_context");

            // 字段解析
            var mapping = EFCoreModel<T>.GetModelMapping(context);

            var table = EFCoreCommon.ListToDataTable<T>(list, mapping.tableName, mapping.rows);

            if (EFCoreTaskParameter.BulkTable.ContainsKey(context))
            {
                foreach (var item in EFCoreTaskParameter.BulkTable[context])
                {
                    if (item.TableName == table.TableName)
                    {
                        item.Merge(table, false, MissingSchemaAction.AddWithKey);
                        return;
                    }
                }

                EFCoreTaskParameter.BulkTable[context].Add(table);
            }
            else
                EFCoreTaskParameter.BulkTable.TryAdd(context, new List<DataTable> { table });

        }

        #endregion

        #region Delete

        /// <summary>
        /// 根据条件删除
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="table"></param>
        /// <param name="where"></param>
        public static void Delete<T>(this DbSet<T> model, Expression<Func<T, bool>> where)
            where T : class, new()
        {

            var context = (DbContext)EFCoreCommon.GetValueByField(model.Local, "_context");

            var tuple = EFCoreCommon.GetWhereStr(context, where);

            var sql = $"delete {tuple.Item1.tableName} where {tuple.Item2}";

            if (EFCoreTaskParameter.dictionary.ContainsKey(context))
                EFCoreTaskParameter.dictionary[context].Add(sql);
            else
                EFCoreTaskParameter.dictionary.TryAdd(context, new List<string>() { sql });
        }

        #endregion

        #region Submit

        /// <summary>
        /// 扩展提交
        /// </summary>
        /// <param name="context"></param>
        /// <param name="error">返回错误信息</param>
        /// <param name="transaction">是否启用事物</param>
        /// <returns></returns>
        public static int BeginSaveChanges(this DbContext context, out string error, bool transaction = true)
        {
            EFCoreTaskParameter.dictionary.TryRemove(context, out List<string> list);
            EFCoreTaskParameter.BulkTable.TryRemove(context, out List<DataTable> tables);
            error = string.Empty;

            Func<SqlConnection, SqlTransaction, int> execute = (connection, tran) => 0;

            if (tables != null && tables.Count > 0)
            {
                execute += (connection, tran) =>
                {
                    foreach (var item in tables)
                    {
                        using (var bulkCopy = transaction ?
                                                new SqlBulkCopy(connection, SqlBulkCopyOptions.CheckConstraints, tran) :
                                                new SqlBulkCopy(connection))
                        {
                            bulkCopy.DestinationTableName = item.TableName;
                            bulkCopy.BulkCopyTimeout = 0;
                            bulkCopy.WriteToServer(item);
                        }
                    }
                    return 1;
                };
            }

            if (list != null && list.Count > 0)
            {
                list = list.Distinct().ToList();

                execute += (connection, tran) => {
                    Console.WriteLine(string.Join(';', list));
                    int row = context.Database.ExecuteSqlCommand(string.Join(';', list));
                    row += context.SaveChanges();
                    return row;
                };
            }

            if (list?.Count > 0 || tables?.Count > 0)
            {
                int rows = 0;
                if (transaction)
                    using (var dbTran = context.Database.BeginTransaction())
                    {
                        try
                        {
                            rows = execute.Invoke(
                                (SqlConnection)context.Database.GetDbConnection(),
                                (SqlTransaction)dbTran.GetDbTransaction());

                            dbTran.Commit();
                        }
                        catch (Exception ex)
                        {
                            dbTran.Rollback();
                            error = ex.Message;
                        }
                    }
                else
                    try
                    {
                        rows = execute.Invoke((SqlConnection)context.Database.GetDbConnection(), null);
                    }
                    catch (Exception ex)
                    {
                        rows = 0;
                        error = ex.Message;
                    }

                return rows;
            }

            return context.SaveChanges();
        }

        /// <summary>
        /// 扩展提交
        /// </summary>
        /// <param name="transaction">是否启用事物</param>
        public static int BeginSaveChanges(this DbContext context, bool transaction = true)
        {
            return context.BeginSaveChanges(out string msg, transaction);
        }

        #endregion
    }
}
